/* 
    封装这个函数的作用：
    参数：
        请求方式：可选参数，可以设置默认值为get
            get
            post
        请求地址:必填参数，
        请求是否异步： 可选参数，默认值为true 
            true
            false
        请求参数:可选参数 默认值为 ''
        回调函数: 必填
            必须是一个函数
    
    因为一些参数是可选的，当有一些参数不传递的时候 会导致实参给形参赋值顺序错误
    可以把参数设置为一个对象

    返回值：不需要，函数中的结果是异步代码执行得到的结果
        异步代码执行的结果只能用回调函数来获取
*/

function ajax(option) {
    // 判断必填的参数是否有填写
    // url  callback
    if (!option.url) {
        // 手动抛出错误
        // 当代码遇到了throw Error() 代码停止不会在执行
        throw Error("url是必填的数据");
    }

    // 判断callback 是否传递
    if (!option.callback) {
        throw Error("callback是必填的数据");
    }
    // 判断传递参数的默认值
    // 如果有传递数据 那么就使用传递数据
    // 如果没有传递数据的时候 就需要设置默认值
    let params = {
        type: option.type || "get",
        async: option.async == false ? false : true,
        url: option.url,
        data: option.data || "",
        callback: option.callback,
        error: option.error || function () {},
    };

    // 参数数据类型的判断
    // type 属性的属性值 只能去 get 和 post
    if (params.type != "get" && params.type != "post") {
        throw Error("请求方式暂时只支持get 和post");
    }

    // 判断callback 是否是严格函数
    // 如果不是函数抛出错误
    // Object.prototype.toString.call(数据) == '[object Function]'
    if (
        Object.prototype.toString.call(params.callback) != "[object Function]"
    ) {
        throw Error("callback 必须是一个函数");
    }

    // data 属性
    // 可以为字符串 和 对象
    // 如果data数据类型不为字符串 也不为对象的时候 抛出异常
    let dataType = Object.prototype.toString.call(params.data);
    if (dataType != "[object String]" && dataType != "[object Object]") {
        throw Error("data的数据类型只能为字符串或者对象");
    }

    // 处理data数据
    // 如果data的值为一个对象的时候 需要把data转换为字符串
    // name=aa&age=18
    if (dataType == "[object Object]") {
        // {name:'aa',age:18}==>name=aa&age=18
        let str = "";
        for (let key in params.data) {
            // console.log(params.data[key]);
            str += key + "=" + params.data[key] + "&";
        }
        params.data = str.substr(0, str.length - 1);
    }

    // 来处理发送请求
    let xhr = new XMLHttpRequest();
    // get 请求怎么传递参数 直接把参数拼接在url,
    // url 和 参数之间用什么拼接

    if (params.type == "get") {
        xhr.open(params.type, params.url + "?" + params.data, params.async);
        xhr.send();
    } else {
        xhr.open(params.type, params.url, params.async);
        xhr.setRequestHeader(
            "Content-Type",
            "application/x-www-form-urlencoded"
        );
        xhr.send(params.data);
    }
    // 判断如果是同步 不需要在onload事件中获取数据
    if (!params.async) {
        params.callback(xhr.responseText);
        return;
    }

    // xhr.onload = function () {
    //     params.callback(xhr.responseText);
    // };
    
    // ajax状态发生改变的时候触发的一个事件
    // 0 1 2 3 4
    xhr.onreadystatechange = function () {
        // console.log(1);
        // http 状态码  xhr.status 3位数
        // xhr.status 状态为1 开头状态表示请求还在继续
        // xhr.status 状态为2 开头状态表示请求成功
        // xhr.status 状态为3 开头状态表示请求成功，缓存，
        // xhr.status 状态为4 开头状态表示请求失败，客户端的错误 404路径错误
        // xhr.status 状态为5 开头状态表示请求失败，服务端的错误

        // 真正的请求成功 应该 是 ajax状态码为4 http 请求的状态码为2 或者3 开头
        if (xhr.readyState == 4 && /^(2|3)\d{2}$/.test(xhr.status)) {
            params.callback(xhr.responseText);
            return;
        }

        // ajax 的值肯定会为 4
        // http的状态码为4 或者 5 开头表示错误
        if (xhr.readyState == 4 && /^(4|5)\d{2}$/.test(xhr.status)) {
            params.error(xhr.responseText);
        }
    };
}

// 直接把promise封装在ajax中
//
function pAjax(option) {
    return new Promise((resolve, reject) => {
        ajax({
            url: option.url,
            type: option.type,
            data: option.data,
            async: option.async,
            callback: function (res) {
                resolve(res);
            },
            // 当ajax请求失败的时候 去执行的一个回调函数
            error: function (err) {
                reject(err);
            },
        });
    });
}
